package Experience;
import Activity.ActivityChooserScreen;
import Activity.ExerciseDescriptorScreen;
import Activity.MealDescriptorScreen;
import Activity.TagChooserScreen;
import Activity.TagManager;
import Comm.Glucose.UltraServer;
import Comm.Net.FollowupPostData;
import Comm.Util.Logger;
import Comm.Util.MaintenanceTask;
import Experience.Screens.GlucoseValueScreen;
import Experience.Screens.IntroScreen;
import Experience.Screens.LogScreen;
import Experience.Screens.MediaChooserScreen;
import Experience.Screens.PreferencesScreen;
import Media.AudioScreen;
import Media.CameraScreen;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.Timer;
import Comm.Net.NetThread;
import Comm.Net.PostData;
import Comm.Net.SamplePostData;
import Comm.Util.FollowupTask;
import Comm.Util.Preferences;
import Experience.Screens.FollowupConfirmationScreen;
import Experience.Screens.FollowupDisplayScreen;
import Experience.Screens.FollowupPromptScreen;

import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.control.VolumeControl;


/**
 *
 * @author  dmgreen
 * @version
 */

//command listener needs to be in canvas; midlet should pass a reference of itself
//into the canvas so the canvas can do a notifyHide() wihen the user presses the
//hide button!!

public class ExpUI extends MIDlet {
    
    public Display display = null;
    
    public static ExpUI MY_MIDLET = null;
    public final static String UPLOADER_URL = "http://ditag.moddweb.com/handset/upload_sample";
    public final static String FUP_UPLOADER_URL = "http://ditag.moddweb.com/handset/upload_followup";
    public final static String TAG_UPDATER_URL = "http://ditag.moddweb.com/handset/update_tags";
    
    
    // some of these are one-off and shouldn't have persistent
    // references to them ...
    
    MediaChooserScreen myCS  = null;
    CameraScreen myCamS      = null;
    AudioScreen myAudioS     = null;
    PreferencesScreen myPreferencesS = null;
    TagChooserScreen myTagChooserS = null;
    
    private TagManager myTagManager;
  
    /* These screens are created dynamnically because they are 'cheap' and
     * better disposed of, as opposed to hanging on to a reference to them.
     *
     * GlucoseValueScreen 
     * ActivityChooserScreen 
     * MealDescriptorScreen
     * ExerciseDescriptorScreen 
    */
    
    public UltraServer myUS   = null;
        
    Displayable lastScreen = null;
    
    int CURRENT = -1;
    
    private static String USER_ID = "4";
    //flag to keep track of if first media has been picked yet.
    
    
    
    /* Several variables that help us manage interaction flow */
    int currentScreen = 0;
    
    private final int SCREEN_GLUCOSE = 1;
    private final int SCREEN_MEDIA_CHOOSER = 2;
    private final int SCREEN_MEDIA_1 = 3;
    private final int SCREEN_MEDIA_2 = 4;
    private final int SCREEN_ACTIVITY_CHOOSER = 5;
    private final int SCREEN_ACTIVITY = 6;
    private final int SCREEN_TAGS = 7;
    private final int SCREEN_CONFIRMATION = 8;
    
    private final int SCREEN_FUP_PROMPT  = 11;
    private final int SCREEN_FUP_GLUCOSE = 12;
    private final int SCREEN_FUP_DATA    = 13;
    private final int SCREEN_FUP_TAGS    = 14;
    private final int SCREEN_FUP_CONFIRMATION = 15;
    
    private int CURRENT_MEDIA = 0;
    public static final int CAMERA = 1;
    public static final int AUDIO  = 2;

    private final int ACTIVITY_CHOICE   = 0;
    private final int ACTIVITY_MEAL     = 1;
    private final int ACTIVITY_EXERCISE = 2;
    
    /* */
    
    
    // how are you going to do navigation for revisit ... ? 
    
    BaseSample currentSample;
    
    private LogScreen myLogS = new LogScreen(this);
    
    private Hashtable mySampleTable = new Hashtable();
    private Timer myMaintenanceTimer = new Timer();
    
    Player notifier;
    
    public ExpUI() {
                
        display = Display.getDisplay(this);
        MY_MIDLET = this;
        
        Logger.createLog();
       
        myCS = new MediaChooserScreen(this);

        myTagManager = new TagManager();
        myTagChooserS = new TagChooserScreen(this, myTagManager);
       

        
        //myGS = new GlucoseServer();
        myUS = new UltraServer();
        
        // every ten minutes == 600000
        myMaintenanceTimer.scheduleAtFixedRate(new MaintenanceTask(mySampleTable), 0, 600000);
        
        Preferences prefs = Preferences.createPreferences();
    }
    
    public static ExpUI getMIDlet() {
        return MY_MIDLET;
    }
    
    public void startApp() {
        //display = Display.getDisplay(this);
        display.setCurrent(new IntroScreen(this,-1,-1));
    
        //myGS.start_server();
        myUS.start_server();
    }
    
    public void pauseApp() {
        
    }
    
    public void destroyApp(boolean unconditional) {
    }
    
    public void notifyNext(String data) {
        /* Establish a mapping between ids and screens */
        
        switch(currentScreen) {
            
            case (SCREEN_GLUCOSE):
                int gluco_val = Integer.parseInt(data);
                System.out.println("Got for glucose value " + gluco_val);
                currentSample.setGlucose(gluco_val);
                display.setCurrent(myCS);
                currentScreen++;
                break;
                
            case (SCREEN_MEDIA_CHOOSER):
                if (data == "CAMERA") {
                    showCameraScreen();
                    CURRENT_MEDIA = CAMERA;
                } else  {
                    showAudioScreen();
                    CURRENT_MEDIA = AUDIO;
                }
                currentScreen++;
                break;
                
            case (SCREEN_MEDIA_1):
                if (CURRENT_MEDIA == CAMERA) {
                    showAudioScreen();
                    CURRENT_MEDIA = AUDIO;
                } else  {
                    showCameraScreen();
                    CURRENT_MEDIA = CAMERA;
                }
                currentScreen++;
                break;
            
                
            case (SCREEN_MEDIA_2):
                System.out.println("SCREEN MEDIA 2, setting to activity chooser");
                display.setCurrent(new ActivityChooserScreen(this));
                System.out.println("OK");
                currentScreen++;
                break;
                
            case (SCREEN_ACTIVITY_CHOOSER ):
                
                if (data == ActivityChooserScreen.MEAL) {
                    ((ExpSample) currentSample).setActivityType(ActivityChooserScreen.MEAL);
                    display.setCurrent(new MealDescriptorScreen(this));
                    currentScreen++;
                    
                } else if (data == ActivityChooserScreen.EXERCISE) {
                     ((ExpSample) currentSample).setActivityType(ActivityChooserScreen.EXERCISE);
                    display.setCurrent(new ExerciseDescriptorScreen(this));
                    currentScreen++;
                    
                } else if (data == ActivityChooserScreen.OTHER) {
                     ((ExpSample) currentSample).setActivityType(ActivityChooserScreen.OTHER);
                    myTagChooserS.repopulate();
                    display.setCurrent(myTagChooserS);
                    currentScreen++;
                    currentScreen++;
                }
                
                break;
                
            case (SCREEN_ACTIVITY):
                // should really make DescriptorData a superclass ...
                if ( ((ExpSample) currentSample).getActivityType() == ActivityChooserScreen.EXERCISE) {
                     ((ExpSample) currentSample).setExerciseDescriptor( ((ExerciseDescriptorScreen) display.getCurrent()).getDescriptorData() );
                } else if (  ((ExpSample) currentSample).getActivityType() == ActivityChooserScreen.MEAL) {
                     ((ExpSample) currentSample).setMealDescriptor( ((MealDescriptorScreen) display.getCurrent()).getDescriptorData() );
                }
                currentScreen++;
                myTagChooserS.repopulate();
                display.setCurrent(myTagChooserS);
                
                break;
            
            case (SCREEN_TAGS):
                currentSample.setTags(((TagChooserScreen) display.getCurrent()).getSelectedTags());
                currentScreen++;
                
                int cCount =  ((ExpSample) currentSample).getImages().size();
                int aCount =  ((ExpSample) currentSample).getAudioClips().size();
            
                display.setCurrent(new IntroScreen(this,cCount,aCount));
                sendSample();
                
                cleanStuffUp();
                
                currentScreen++;
                break;
            
                
            case (SCREEN_CONFIRMATION):
                //  Should actually never get here ...
                break;
                
            case (SCREEN_FUP_PROMPT ):
                
                display.setCurrent(new GlucoseValueScreen(this));
                currentScreen++;
                
                Logger.getLog().addLogMessage("currentSample is " + currentSample, Logger.MSG_INFO);
                
                break;
                
            case (SCREEN_FUP_GLUCOSE):
                int fup_gluco_val = Integer.parseInt(data);
                
                ((FollowupSample) currentSample).setPostGlucose(fup_gluco_val);
                display.setCurrent( new FollowupDisplayScreen((FollowupSample)currentSample) );
                currentScreen++;
                break;
            
            case (SCREEN_FUP_DATA):
               myTagChooserS.repopulate();
               display.setCurrent(myTagChooserS);
               currentScreen++;
               break;
            
            case (SCREEN_FUP_TAGS):
                currentSample.setTags(((TagChooserScreen) display.getCurrent()).getSelectedTags());
                
                display.setCurrent(new FollowupConfirmationScreen(this));
                
                // send the current (followup) sample
                sendSample();
                cleanStuffUp();
                
                currentScreen++;
                
                break;

            case (SCREEN_FUP_CONFIRMATION):
                //  Should actually never get here ...
            break;
            
        } // END switch statement       
    }
    
    public void sendSample() {
        log("sendSample called");
        if (currentSample == null) {
            log("currentSample is null, returning");
            return;
        }
        
        PostData pd = null;
        log("Checking ...");
        log ("TYPE IS: " + currentSample.getType() );
         
        if ( currentSample.getType() == "Experience") {
            log("Got an exp sample");
            pd = new SamplePostData( (ExpSample) currentSample );
        }
        
        else if ( currentSample.getType() == "Followup") {
            log("Got a followup sample");
            pd = new FollowupPostData((FollowupSample) currentSample);
        }
        
        NetThread nt = new NetThread(pd);
        mySampleTable.put(new Integer(pd.getUID()), pd);
        nt.start();
        
        // maybe need to make a Hashtable here that maps postdata to timer objs?
        // so when the cleanup thread comes through it can check to see if the
        // data object has been sent; if it has it will cancel the thread, and
        // if it hasn't, it will reschedule
        System.gc();
    }
    
    public void notifyClose(boolean from_followup) {
        
        // NOTE: cleaning up the camera and audio resources has already
        // happened as soon as the final screen is arrived at!
        
        if (from_followup) {
            // this is an unideal workaround  - it's not possible to set to intro screen then set to null'
            display.setCurrent(new IntroScreen(this,-1,-1));
        } else {
            display.setCurrent(null);
        }
        currentScreen = 0; 
    }
        
     public void showAlert(String msg) {
        Alert a = new Alert("Uh oh", msg, null, AlertType.WARNING);
        a.setTimeout(Alert.FOREVER);
        display.setCurrent(a);     
    }
    
    public void log(String msg) {
        // NEED to parameterize this!
        System.out.println(msg);
        Logger.getLog().addLogMessage(msg, Logger.MSG_DEBUG);
    }
    
    public void showLogScreen() {
        lastScreen = display.getCurrent();
        myLogS.deleteAll();
        myLogS.append(Logger.getLog().getLogMessages());
        display.setCurrent(myLogS);
    }
    
    public LogScreen isLogScreenDisplayed() {
        if (display.getCurrent() == myLogS) {
            return myLogS;
        } else {
            return null;
        }
    }
    
    public void showPreferencesScreen() {
        if (myPreferencesS == null) {
            myPreferencesS = new PreferencesScreen(this);
        }
        lastScreen = display.getCurrent();
        
        myPreferencesS.reconstitute();
        display.setCurrent(myPreferencesS);
    }
    
    public void unshowPreferencesScreen() {
        if (lastScreen != null) {
            display.setCurrent(lastScreen);
        }
        lastScreen = null;
    }
    
    private void showCameraScreen() {
        if (myCamS == null) {
            myCamS = new CameraScreen(this);
        } else {
            myCamS.disable();
            myCamS.showViewfinder();
            myCamS.enable();
        }
                
        display.setCurrent(myCamS);
    }
    
    private void showAudioScreen() {
        if (myAudioS == null) {
            myAudioS = new AudioScreen(this);
        }

        display.setCurrent(myAudioS);
    }

    public void startNewFollowup(int local_sample_uid) {
        // guarantee that this won't get called while person is recording a sample
        // sample was already created in maintenencetask
        
        cleanStuffUp();
    
        FollowupSample fs = (FollowupSample) mySampleTable.get(new Integer(local_sample_uid));
        currentSample = fs;
    
        currentScreen = SCREEN_FUP_PROMPT;
        display.setCurrent(new FollowupPromptScreen(this));
    }
    
    public void notifyFollowupDelay() {
        FollowupSample fup = (FollowupSample) currentSample;
        int local_sample_id = fup.getLocalSampleID();
        // five minutes
        (new Timer()).schedule( new FollowupTask(local_sample_id), 60000 );
    
        currentSample = null;
        
        notifyClose(true);
    }
    
    public void startNewSample() {
        
        cleanStuffUp();
                
        ExpSample es = new ExpSample();
        currentSample = es;
        
        // start out with glucose screen.
        currentScreen = SCREEN_GLUCOSE;
        display.setCurrent(new GlucoseValueScreen(this));
    }
    
    private void cleanStuffUp() {
        try {
            if (myCamS != null) {
                myCamS.cleanUp();
            }
        
            if (myAudioS != null) {
                myAudioS.cleanUp();
            }
        } catch (Exception e) {
            Logger.getLog().addLogMessage("Error while cleaningstuffup", Logger.MSG_ERROR);
        }
        
        currentSample = null;
    }
    
    public void setAddress(String newAddress) {
        //myMessageDaemon.setAddress(newAddress);
    }
    
    public static void setUserID(String user_id) {
        USER_ID = user_id;
    }
    
    public static String getUserID() {
        return USER_ID;
    }
    
    public String getAddress() {
        //return myMessageDaemon.getAddress();
        return "";
    }
     
    public void stopRinging() {
        if ( (notifier != null) && (notifier.getState() == Player.STARTED) ) {
            try {
                notifier.stop();
            }
            catch (MediaException me) {
                
            }
        }
    }
    
    public void startRinging() {
        // Brrrrrring Brrrrrrrring !!
        InputStream ring_stream = getClass().getResourceAsStream("/simplering.mid");
        try {
            notifier = Manager.createPlayer(ring_stream, "audio/midi");
            notifier.setLoopCount(1);
            notifier.realize();
            
            VolumeControl vc = (VolumeControl) notifier.getControl("VolumeControl");
            vc.setLevel(25);
            
            notifier.start();
        } catch (MediaException me) {
            return;
        } catch (IOException ioe2) {
            return;
        }
    }
    
    public BaseSample getCurrentSample() {
        return currentSample;
    }
     
    public TagManager getTagManager() {
        return myTagManager;
    }
    
    public void addGlucoseToSample(int gluco_reading) {
        if (currentSample != null) {
            log("Adding " + gluco_reading + " to sample");
            currentSample.setGlucose(gluco_reading);
        }          
        else {
            log("Not adding gluco because currentSample is null");
        }
    }
    
    public void addImageToSample(byte[] image_data) {
        if (currentSample != null) {
             ((ExpSample) currentSample).addImage(image_data);
        }
    }
    
    public void addAudioToSample(byte[] audio_data) {
        if (currentSample != null) {
             ((ExpSample) currentSample).addAudioClip(audio_data);
        }
    }
    
    public void deleteLastAudio() {
        if (currentSample != null) {
             ((ExpSample) currentSample).deleteLastAudio();
        }
    }
    
    public void deleteLastImage() {
        if (currentSample != null) {
             ((ExpSample) currentSample).deleteLastImage();
        }
    }
    
    /* Think I don't need this ..
    public int getCurrentGlucose() {
        if (currentSample != null) {
            return currentSample.getPreGlucose();
        }       
        else {
            return 9999;
        }
    }
     */
}
